use std::cmp::Reverse;
use std::collections::HashMap;
use std::ops::{Add, Deref};
use std::rc::Rc;
use std::time::Duration;
use chrono::{Local, TimeDelta};
use gloo::timers::callback::Timeout;
use web_sys::{InputEvent};
use yew::{classes, function_component, html, use_context, use_mut_ref, use_state, Callback, Html, Properties};
use yew_hooks::{use_effect_once, use_local_storage};
use yewdux::{use_selector_with_deps, use_store, Dispatch, Reducer, Store};
use crate::db::pojo::pojo::ImSession;
use crate::db::service::im_session_service::ImSessionService;
use crate::business::app_state::AppState;
use crate::business::page::fragment::user_icon::UserIcon;
use crate::business::util::input::get_value_from_input_event;
use crate::business::util::util::get_login_info;
use crate::util::time::format_session_time;

#[derive(Properties, Clone, PartialEq)]
pub struct ImSessionListViewProps {
    pub(crate) select_session_callback: Callback<ImSession>,
}

#[function_component(ImSessionListView)]
pub fn im_session_list_view(props: &ImSessionListViewProps) -> Html {
    let app_state = use_context::<AppState>().unwrap();
    let (state, dispatch) = use_store::<SessionListState>();
    let search_state = use_state(|| String::new());

    let app_state_clone = app_state.clone();

    let local_storage = use_local_storage::<String>("_login_".to_string());
    let user_id = match get_login_info(local_storage) {
        None => {None}
        Some(token) => {
            Some(token.use_id)
        }
    };

    use_effect_once(move || {
        load_data(user_id.unwrap(), app_state_clone, dispatch.clone());

        ||()
    });
    
    let search_state_clone = search_state.clone();
    let search_callback = Callback::from(move |search: String| {
        log::info!("search value: {}", search);
        search_state_clone.set(search);
    });

    let select_session_callback = props.select_session_callback.clone();

    html!{
        <div class="im-session-container">
            <SessionSearchView value="" {search_callback} />
            <ul class="im-session-list minimal">
                { state.data.iter().filter(| session | session.deref().name.contains(search_state.deref()))
                    .map(|data| html! {
                      <SessionItemView session = {data.clone()} select_session_callback={select_session_callback.clone()}/>
                     })
                    .collect::<Vec<Html>>()
                }
            </ul>
        </div>
    }
}

#[derive(Debug, Clone, PartialEq, Default, Store)]
pub struct SessionListState {
    pub data: Vec<ImSession>,
    pub selected_session: Option<u64>,
    pub hot_message: HashMap<u64, usize>,
    pub hot_message_total: usize,
}

pub enum SessionAction {
    UpdateSession(ImSession),
    RemoveSession(ImSession),
    AddSession(ImSession),
    AddSessions(Vec<ImSession>),
    SelectSession(Option<u64>),
    IncrementHotMessages(HashMap<u64, usize>),
    DecreaseHotMessages(HashMap<u64, usize>),
    SetHotMessageTotal(Vec<ImSession>),
}

#[derive(Properties, Clone, PartialEq)]
struct SessionSearchProps {
    #[prop_or_default]
    value: String,
    search_callback: Callback<String>,
}

#[function_component(SessionSearchView)]
fn session_search_view(props: &SessionSearchProps) -> Html {
    let SessionSearchProps { value, search_callback } = props.clone();

    let search_content_ref = use_mut_ref(|| String::new());

    let search_content_ref_clone = search_content_ref.clone();
    let search_callback_clone = search_callback.clone();
    let text_input_callback = Callback::from(move |input_event: InputEvent| {
        let search = get_value_from_input_event(input_event);
        *search_content_ref_clone.borrow_mut() = search.clone();

        let tt = search_content_ref_clone.clone();
        let search_callback_clone2 = search_callback_clone.clone();
        log::info!("input search: {}", search);

        let _ = Timeout::new(Duration::from_millis(300).as_millis() as u32, move || {
            let old_search = tt.borrow().deref().clone();
            let search_clone = search.clone();
            log::info!("input search22: {}, {}", old_search, search_clone);
            if old_search == search_clone {
                search_callback_clone2.emit(search_clone);
            }
        }).forget();
    });
    html! {
        <div class="im-session-search-view">
            <input type="text" oninput={text_input_callback} style="min-width: 150px" />
            <img src="/imgs/plus.png" width="15" height="15"/>
        </div>
    }
}


#[derive(Properties, Clone, PartialEq)]
struct SessionItemProps {
    pub session: ImSession,
    pub select_session_callback: Callback<ImSession>,
}

#[function_component(SessionItemView)]
fn session_item_view(props: &SessionItemProps) -> Html {
    let selected_session_callback = props.select_session_callback.clone();
    let session2 = props.session.clone();
    let session = props.session.clone();

    let (state, dispatch) = use_store::<SessionActive>();
    let (session_state, session_dispatch) = use_store::<SessionListState>();

    let is_selected = state.value == session.server_id.clone().unwrap();

    let select_css = if is_selected {
        "active"
    } else {
        ""
    };

    // let selected_session_callback2 = selected_session_callback.reform(move |_| session2.clone());
    let selected_session_callback2 = Callback::from(move |_| {
        let session3 = session2.clone();
        let server_id = session3.server_id.clone().unwrap();
        let session_id = session3.id;
        dispatch.reduce_mut(|store| store.value = server_id);
        session_dispatch.apply(SessionAction::SelectSession(session_id));
        selected_session_callback.emit(session3);
    });

    let session_id = props.session.id.clone().unwrap();
    let session_hot_message_total = use_selector_with_deps(|state : &SessionListState, id| {
        log::info!("select session hot data: {:?}, {}", state.hot_message, id);
        state.hot_message.get(id).cloned().unwrap_or_default()
    }, session_id);
    let is_show = if *session_hot_message_total > 0 { "" } else { "hidden" };
    
    html! {
        <li>
            <div class={classes!("im-session-item", select_css)} onclick = {selected_session_callback2}>
                <icon>
                    <UserIcon/>
                    <span class={classes!("badge", "circle-badge", is_show) }> { *session_hot_message_total }</span>
                </icon>
                <name><span title={ session.name.clone() }>{ session.name.clone() }</span> </name>
                <message> {session.lately_message.clone()} </message>
                <close><img src="/imgs/close.png" width="15px" height="15px"/></close>
                <time>{ format_session_time(&session.update_time) }</time>
            </div>
        </li>
    }
}

fn load_data(user_id: String, _app_state: AppState, dispatch: Dispatch<SessionListState>) {
    let session_service = ImSessionService;
    let _start_time = Local::now().add(TimeDelta::days(-14));
    let session_list = session_service.query(&user_id, 0, 1000);
    // log::info!("loading session data, {:?}", session_list);
    // use_list_handle.append(&mut session_list);

    let hot_message_map : HashMap<_, _> = session_list.iter().map(|session| (session.id.unwrap(), session.hot_message_total.clone() as usize)).collect();
    log::info!("hot message data: {:?}", hot_message_map);
    dispatch.apply(SessionAction::AddSessions(session_list));
}

impl Reducer<SessionListState> for SessionAction {
    fn apply(self, mut state: Rc<SessionListState>) -> Rc<SessionListState> {
        match self {
            SessionAction::UpdateSession(session) => {
                let mut data = state.data.clone();
                let mut hot_message = state.hot_message.clone();
                *hot_message.entry(session.id.clone().unwrap()).or_insert(0) = session.hot_message_total.clone() as usize;
                
                if let Some(index) = data.iter().position(|t| t.id.unwrap() == session.id.unwrap()) {
                    log::info!("update session index: {}", index);
                    data[index] = session;
                } else {
                    data.push(session);
                }
                let hot_message_total = data.iter().map(|session| session.hot_message_total.clone() as usize).sum::<usize>();
                log::info!("update sessions: {:?}, {}", data.len(), hot_message_total);
                data.sort_by_key(|t| Reverse(t.update_time));
                Rc::new(SessionListState {
                    data,
                    selected_session: state.selected_session.clone(),
                    hot_message_total,
                    hot_message,
                })
            }
            SessionAction::RemoveSession(session) => {
                let mut data = state.data.clone();
                data.retain(|old_session| session.id != old_session.id);
                Rc::new(SessionListState {
                    data,
                    selected_session: state.selected_session.clone(),
                    hot_message_total: state.hot_message_total.clone(),
                    hot_message: state.hot_message.clone(),
                })
            }
            SessionAction::AddSession(session) => {
                let mut data = state.data.clone();
                let mut is_existed = false;
                for session in data.iter() {
                    if session.id == session.id {
                        is_existed = true;
                        break;
                    }
                }
                if !is_existed {
                    data.push(session);
                }
                Rc::new(SessionListState {
                    data,
                    selected_session: state.selected_session.clone(),
                    hot_message_total: state.hot_message_total.clone(),
                    hot_message: state.hot_message.clone(),
                })
            }
            SessionAction::AddSessions(sessions) => {
                let mut data = state.data.clone();
                let mut session_list = vec![];
                let mut hot_message_total = state.hot_message_total.clone();
                let mut hot_message = state.hot_message.clone();
                let selected_session = state.selected_session.clone();

                for new_session in sessions {
                    hot_message.entry(new_session.id.clone().unwrap()).or_insert(0);
                    let mut is_existed = false;
                    for session in data.iter() {
                        if new_session.id == session.id {
                            is_existed = true;
                            break;
                        }
                    }
                    if !is_existed {
                        session_list.push(new_session);
                    }
                }

                data.extend(session_list);
                hot_message_total = data.iter().map(|session| session.hot_message_total.clone() as usize).sum::<usize>();

                Rc::new(SessionListState {
                    data,
                    selected_session,
                    hot_message_total,
                    hot_message,
                })
            }
            SessionAction::SelectSession(session_id) => {
                Rc::new(SessionListState {
                    data: state.data.clone(),
                    selected_session: session_id,
                    hot_message_total: state.hot_message_total.clone(),
                    hot_message: state.hot_message.clone(),
                })
            }
            SessionAction::IncrementHotMessages(map) => {
                log::info!("increase hot messages, data: {:?}", map);
                let selected_session = state.selected_session.clone();
                let mut hot_message_total = state.hot_message_total.clone();
                let mut hot_message = state.hot_message.clone();
                match selected_session {
                    None => {
                        map.into_iter().for_each(|(session_id, size)| {
                            *hot_message.entry(session_id.clone()).or_insert(0) += size;
                        });
                    }
                    Some(selected_session) => {
                        map.into_iter().for_each(|(session_id, size)| {
                            if session_id != selected_session {
                                *hot_message.entry(session_id.clone()).or_insert(0) += size;
                            }
                        });
                    }
                }

                let tt = hot_message.iter().map(|(key, value)| value.clone()).filter(|value| value > &0).collect::<Vec<_>>();
                log::info!("increase hot messages, data1: {:?}", tt);
                hot_message_total = tt.into_iter().sum::<usize>();
                log::info!("increase hot messages, data2: {:?}", hot_message_total);
                Rc::new(SessionListState {
                    data: state.data.clone(),
                    selected_session,
                    hot_message_total,
                    hot_message,
                })
            }
            SessionAction::DecreaseHotMessages(map) => {
                log::info!("decrease hot message total, {:?}, {:?}, {:?}", map, state.hot_message_total, state.hot_message);
                let selected_session = state.selected_session.clone();
                let mut hot_message_total = state.hot_message_total.clone() as i32;
                let mut hot_message = state.hot_message.clone();
                match selected_session {
                    None => {
                        log::info!("hot message info, {:?}", state.hot_message);
                    }
                    Some(selected_session) => {
                        map.into_iter().for_each(|(session_id, size)| {
                            if session_id == selected_session {
                                hot_message_total -= size as i32;
                                let t = hot_message.entry(session_id.clone()).or_insert(0);
                                if *t > size {
                                    *t -= size;
                                } else {
                                    *t = 0;
                                }
                            }
                        });
                    }
                }

                let hot_message_total = if hot_message_total > 0 {hot_message_total as usize} else {0};
                log::info!("decrease hot message total2, {:?}", hot_message_total);

                Rc::new(SessionListState {
                    data: state.data.clone(),
                    selected_session,
                    hot_message_total,
                    hot_message,
                })
            }
            SessionAction::SetHotMessageTotal(sessions) => {
                log::info!("set_hot_message_total, {:?}", sessions);
                let mut hot_message_total = 0 as usize;
                let mut hot_message = HashMap::new();
                for session in sessions {
                    hot_message_total += session.hot_message_total.clone() as usize;
                    *hot_message.entry(session.id.clone().unwrap()).or_insert(0) += session.hot_message_total as usize;
                }
                log::info!("set_hot_message_total, {:?}", hot_message_total);
                Rc::new(SessionListState {
                    data: state.data.clone(),
                    selected_session: state.selected_session.clone(),
                    hot_message_total,
                    hot_message,
                })
            }
        }
    }
}

#[derive(Default, Clone, PartialEq, Store)]
pub struct SessionActive {
    pub value: String,
}